﻿using System;
using Dapper;
using System.Data;
using EaseDapper.Enums;
using EaseDapper.Interface;
using Microsoft.Data.Sqlite;
using MySql.Data.MySqlClient;
using System.Data.SqlClient;
using static Dapper.DapperCRUD;

namespace EaseDapper.Abstract
{
    public class DapperExecute : IExecute
    {
        #region Fields
        private static DapperExecute _execute;
        private static readonly object obj_execute_lock = new object();
        #endregion

        private DapperExecute() { }

        public static DapperExecute Instance
        {
            get
            {
                if (_execute is null)
                {
                    lock (obj_execute_lock)
                    {
                        _execute = new DapperExecute();
                    }
                }
                return _execute;
            }
        }

        /// <summary>
        /// 执行sql操作
        /// </summary>
        /// <param name="sql">sql语句</param>
        /// <param name="Parameters">参数</param>
        /// <returns></returns>
        public int Execute(string sql, object Parameters = null)
        {
            return ExecuteWithTransaction((conn, trans) =>
            {
                int r = conn.Execute(sql, Parameters, trans);
                return r;
            });
        }

        /// <summary>
        ///  无事务操作，用于查询
        /// </summary>
        /// <param name="action"></param>
        public void ExecuteWithoutTransaction(Action<IDbConnection> action)
        {
            UseConnectObj(action);
        }

        /// <summary>
        /// 事务操作，用于增、删、改
        /// </summary>
        /// <returns>Execute Result</returns>
        /// <param name="func"></param>
        public int ExecuteWithTransaction(Func<IDbConnection, IDbTransaction, int> func)
        {
            int r = 0;
            IDbTransaction trans = null;
            UseConnectObj(conn =>
            {
                try
                {
                    trans = conn.BeginTransaction();
                    r = func(conn, trans);
                    trans.Commit();
                }
                catch (Exception ex)
                {
                    trans.Rollback();
                    throw new Exception(ex.ToString());
                }
            });
            return r;
        }

        /// <summary>
        /// Use Action Connection
        /// </summary>
        /// <param name="action"></param>
        private void UseConnectObj(Action<IDbConnection> action)
        {
            try
            {
                using IDbConnection conn = GetOpenConnection(GlobalDatas.Config.DbType, GlobalDatas.Config.ConnectString);
                conn.Open();
                action(conn);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.ToString());
            }
        }

        private IDbConnection GetOpenConnection(string _dbtype, string connstr)
        {
            IDbConnection connection = null;
            if (_dbtype == Dialect.Sqlite.ToString())
            {
                connection = new SqliteConnection(connstr);
                SetDialect(Dialect.Sqlite);
            }
            else if (_dbtype == Dialect.SqlServer.ToString())
            {
                connection = new SqlConnection(connstr);
                SetDialect(Dialect.SqlServer);
            }
            else if (_dbtype == Dialect.MySql.ToString())
            {
                connection = new MySqlConnection(connstr);
                SetDialect(Dialect.MySql);
            }
            return connection;
        }
    }
}